﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;

namespace Dycox.Workflows
{
    partial class WFThread : SupportAppData, IWorkflowComponent, ICloneable
    {
        internal WFStep NewStep(Stage stage)
        {
            WFStep step = new WFStep();

            step.Sequence = this.Process.StepSeed++;
            step.Thread = this;
            step.CreationTime = WorkflowManager.TimeService.Now;
            step.StageName = stage.Name;
            
            return step;
        }

        public WFStep ActiveStep
        {
            get
            {
                WFStep step = null;

                foreach (WFStep s in StepsInternal)
                {
                    if (s.Status == WorkflowComponentStatus.Active)
                    {
                        if (step != null)
                            throw new WorkflowException("Thread " + ((IWorkflowComponent)this).ID + " has more than one active steps.");
                        step = s;
                    }
                }

                return step;
            }
        }

        public WFStep FirstStep
        {
            get
            {
                return StepsInternal.OrderBy(o => o.Sequence).FirstOrDefault();
            }
        }

        public WFStep[] Steps
        {
            get { return StepsInternal.ToArray(); }
        }

        public WFStep GetStep(int sequence)
        {
            return StepsInternal.SingleOrDefault(o => o.Sequence == sequence);
        }

        public WFStep GetPreviousStep(WFStep step)
        {
            if (step == null)
                throw new ArgumentNullException("step");
            if (step.ProcessId != this.ProcessId && step.ThreadId != this.ThreadId)
                throw new WorkflowException("The given step is not belong to this thread.");

            return StepsInternal.Where(o => o.Sequence < step.Sequence).OrderByDescending(o => o.Sequence).FirstOrDefault();
        }

        public bool IsMainThread
        {
            get
            {
                return this.ProcessId != Guid.Empty && this.ThreadId == WFProcess.MainThreadId;
            }
        }

        public WFStep LastStep
        {
            get
            {
                return StepsInternal.OrderByDescending(o => o.Sequence).FirstOrDefault();
            }
        }

        public WFStep GetStep(short sequence)
        {
            return StepsInternal.SingleOrDefault(o => o.Sequence == sequence);
        }

        public WFThread[] GetChildThreads()
        {
            return Process.ThreadsInternal.Where(o => o.ParentThreadId == this.ThreadId && o != this).ToArray();
        }

        public WFThread ParentThread
        {
            get
            {
                if (!IsMainThread)
                {
                    WFThread parent = Process.ThreadsInternal.SingleOrDefault(o => o.ThreadId == this.ParentThreadId);
                    if (parent == null)
                        throw new WorkflowException("Missing parent thread: " + this.ParentThreadId);
                    return parent;
                }
                else
                    return null;
            }
        }

        public bool IsSuspended
        {
            get
            {
                return FlowControl.IsSuspendedCore(this);
            }
        }

        public bool Critical
        {
            get
            {
                return FlowControl.IsCriticalCore(this);
            }
        }

        private ThreadProperties _properties;

        internal ThreadProperties Properties
        {
            get
            {
                if (_properties == null)
                    _properties = CreateProperties();
                return _properties;
            }
        }

        private ThreadProperties CreateProperties()
        {
            ThreadProperties tp ;
            if (_PropertiesData == null)
                tp = new ThreadProperties();
            else
                tp = ThreadProperties.Parse(_PropertiesData);

            tp.PropertyChanged += PropertiesPropertyChanged;

            return tp;
        }

        private void PropertiesPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            this.PropertiesData = sender.ToString();
        }

        public ComponentIdentifier ID
        {
            get
            {
                return new ComponentIdentifier(_ProcessId, _ThreadId);
            }
        }

        IWorkflowComponent IWorkflowComponent.OwnerComponent
        {
            get
            {
                return this.Process;
            }
        }

        public WFThread Clone()
        {
            WFThread thread = (WFThread)this.MemberwiseClone();

            thread.Initialize();
            foreach (var s in StepsInternal)
                thread.StepsInternal.Add(s.Clone());

            return thread;
        }

        object ICloneable.Clone()
        {
            return this.Clone();
        }
    }
}
